package buymyhome.com.util;

import buymyhome.com.pojo.UsedHouse;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.api.R;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import static buymyhome.com.common.SystemConstants.*;
import static buymyhome.com.common.SystemConstants.HOUSE_TIME;
import static buymyhome.com.util.DoLock.tryLock;
import static buymyhome.com.util.DoLock.unlock;

/**
 * @author 杨世博
 */
@Component
public class CacheClient {
    private final StringRedisTemplate stringRedisTemplate;

    public CacheClient(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 修改Redis中的内容
     * @param key
     * @param value
     * @param time
     * @param unit
     */
    public void set(String key, Object value, Long time, TimeUnit unit){
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);
    }

    /**
     * 根据id查找  互斥锁解决缓存击穿
     * @param keyPrefix
     * @param id
     * @param type
     * @param dbFallback
     * @param time
     * @param unit
     * @param <R>
     * @param <ID>
     * @return
     */
    public <R, ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit){
        String key = keyPrefix + id;

        R r;

        //1、从 redis 查询房源缓存
        String json = stringRedisTemplate.opsForValue().get(key);
        //2、判断是否存在
        if (StrUtil.isNotBlank(json)){
            //3、存在直接返回
            return JSONUtil.toBean(json, type);
        }
        //判断命中是否为空值
        if (json != null) {
            return null;
        }

        //实现缓存重建
        //4.1 获取互斥设备锁
        String lockKey = LOCK_HOUSE + id;

        try {
            boolean isLock = tryLock(lockKey, stringRedisTemplate);
            //4.2 判断是否获取成功
            if (!isLock) {
                //4.3 失败，休眠并重试
                Thread.sleep(50);
                return queryWithPassThrough(keyPrefix, id, type, dbFallback, time, unit);
            }

            //4、不存在根据id到数据库种查询
             r = dbFallback.apply(id);
            //5、不存在，返回错误
            if(r == null || ((UsedHouse) r).getDeleted() == 1){
                stringRedisTemplate.opsForValue().set(key, "", HOUSE_NULL_TIME, TimeUnit.MINUTES);
                return null;
            }
            //6、存在，写入redis
            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonPrettyStr(r), time, unit);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            //释放互斥锁
            unlock(lockKey, stringRedisTemplate);
        }

        return r;
    }

}
